package homeworks.cjt2009.homework1.tsetso.solution;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.TreeSet;

public class MyHotel implements Hotel {

	// --- Fields --------------------------------------------------------------
	private TreeSet<Room> rooms = null;
	private HashMap<Room, Collection<Guest>> roomGuests = null;

	Comparator<Room> capacityRoomComparator = new Comparator<Room>() {
		@Override
		public int compare(Room r1, Room r2) {
			return r1.getCapacity() - r2.getCapacity();
		}
	};

	Comparator<Room> rateRoomComparator = new Comparator<Room>() {
		@Override
		public int compare(Room room1, Room room2) {
			return (int) (room1.getRate() - room2.getRate());
		}
	};

	private enum LineFlag {
		R, G
	};

	// --- Constructors --------------------------------------------------------
	public MyHotel() {
		this(new TreeSet<Room>(), new HashMap<Room, Collection<Guest>>());
	}

	public MyHotel(TreeSet<Room> rooms) {
		this(rooms, generateRoomGuestMap(rooms));
	}

	public MyHotel(HashMap<Room, Collection<Guest>> roomGuests) {
		this(new TreeSet<Room>(roomGuests.keySet()), roomGuests);
	}

	public MyHotel(TreeSet<Room> rooms,
			HashMap<Room, Collection<Guest>> roomGuests) {
		this.rooms = new TreeSet<Room>(rooms);
		this.roomGuests = new HashMap<Room, Collection<Guest>>(roomGuests);
	}

	public static HashMap<Room, Collection<Guest>> generateRoomGuestMap(
			Collection<Room> rooms) {
		HashMap<Room, Collection<Guest>> resultMap = new HashMap<Room, Collection<Guest>>();
		for (Room room : rooms) {
			resultMap.put(room, new HashSet<Guest>());
		}
		return resultMap;
	}

	// --- Getters & Setters ---------------------------------------------------
	public HashMap<Room, Collection<Guest>> getRoomGuests() {
		return roomGuests;
	}

	public void setRoomGuests(HashMap<Room, Collection<Guest>> roomGuests) {
		this.roomGuests = roomGuests;
	}

	public TreeSet<Room> getRooms() {
		return rooms;
	}

	// --- Methods -------------------------------------------------------------

	@Override
	public Room rentARoom(Collection<Guest> guests)
			throws NoAvailableRoomException {

		List<Room> listRooms = new ArrayList<Room>();
		listRooms.addAll(rooms);
		Collections.sort(listRooms, capacityRoomComparator);
		TreeSet<Room> roomsSotredByCapacity = new TreeSet<Room>(capacityRoomComparator);
		roomsSotredByCapacity.addAll(listRooms);

		// test
		System.out.println("roomsSotredByCapacity -->" + roomsSotredByCapacity + "\n\n");

		roomsSotredByCapacity = (TreeSet<Room>) roomsSotredByCapacity
				.tailSet(new Room(0, guests.size(), 0.0f));

		for (Room room : roomsSotredByCapacity) {
			for (Map.Entry<Room, Collection<Guest>> pair : roomGuests
					.entrySet()) {
				if ((pair.getValue().isEmpty()) && (pair.getKey().equals(room))) {
					pair.getValue().addAll(guests);
					return pair.getKey();
				}
			}
		}
		throw new NoAvailableRoomException();
	}

	@Override
	public Room rentARoom(Collection<Guest> guests, float rate)
			throws NoAvailableRoomException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void populateRooms(Collection<Room> rooms) {
		for (Room room : rooms) {
			getRooms().add(room);
			getRoomGuests().put(room, new HashSet<Guest>());
		}
	}

	@Override
	public Collection<Room> getAvailableRooms() {
		Collection<Room> availableRooms = new HashSet<Room>();
		for (Map.Entry<Room, Collection<Guest>> pair : roomGuests.entrySet()) {
			if (pair.getValue().isEmpty()) {
				availableRooms.add(pair.getKey());
			}
		}
		return (availableRooms.isEmpty()) ? null : availableRooms;
	}

	@Override
	public Collection<Guest> getGuestInRoom(int roomNumber) {
		Collection<Guest> guestsInRoom = new HashSet<Guest>();
		for (Map.Entry<Room, Collection<Guest>> pair : roomGuests.entrySet()) {
			if (pair.getKey().getNumber() == roomNumber) {
				System.out.println("In IF clause");
				guestsInRoom.addAll(pair.getValue());
				return guestsInRoom.isEmpty() ? null : guestsInRoom;
			}
		}
		return null;
	}

	@Override
	public int getRoom(Guest guest) {
		if (guest == null) {
			throw new IllegalArgumentException("Null-pointed argument "
					+ "passed to getRoom Method() in MyHotel class");
		}

		for (Map.Entry<Room, Collection<Guest>> pair : roomGuests.entrySet()) {
			if (pair.getValue().contains(guest)) {
				return pair.getKey().getNumber();
			}
		}
		return -1;
	}

	@Override
	public boolean store(File f) {
		if (f == null) {
			return false;
		}

		PrintStream output = null;
		try {
			output = new PrintStream(f, "UTF-8");
			writeData(output);
		} catch (FileNotFoundException fnfe) {
			System.out.println("Loaded file not found.");
			return false;
		} catch (UnsupportedEncodingException usee) {
			System.out.println("Passed Encoding is not supported.");
			return false;
		} catch (IOException ioe) {
			System.out.println("Problem during loading"
					+ " Hotel status from file.");
			return false;
		} finally {
			if (output != null) {
				output.close();
			}
		}
		return true;
	}

	private void writeData(PrintStream output) throws IOException {
		for (Map.Entry<Room, Collection<Guest>> pair : roomGuests.entrySet()) {
			if (roomGuests.get(pair.getKey()).isEmpty()) {
				output.println(pair.getKey() + " is available.");
			} else {
				output.println(pair.getKey() + " is rented by:");
				for (Guest guest : pair.getValue()) {
					output.println("\t" + guest);
				}
			}
			output.println();
		}
	}

	@Override
	public boolean load(File f) {

		if (f == null) {
			return false;
		}

		Scanner input = null;
		try {
			input = new Scanner(f, "UTF-8");
			readData(input);
		} catch (FileNotFoundException e) {
			System.out.println("Loaded file not found.");
			return false;
		} catch (IOException ioe) {
			System.out.println("Problem during loading"
					+ " Hotel status from file.");
			return false;
		} finally {
			if (input != null) {
				input.close();
			}
		}
		return true;
	}

	private void readData(Scanner input) throws IOException {

		Room room = null;
		Set<Guest> setGuests = new HashSet<Guest>();

		String currentLine = null;
		String[] lineElems = null;

		while (input.hasNextLine()) {
			currentLine = input.nextLine();
			StringBuilder guestFullName = new StringBuilder();

			// System.out.println(currentLine);
			if (currentLine.startsWith(LineFlag.R.toString())) {
				lineElems = currentLine.substring(2).split("[ ]+");
				int roomNumber = Integer.parseInt(lineElems[0]);
				int roomCapacity = Integer.parseInt(lineElems[1]);
				float roomRate = Float.parseFloat(lineElems[2]);

				// before to save next Room in the TreeSet rooms ,
				// add the pair <Room,Collection<Guest>> in the HashMap
				if (room != null) {
					roomGuests.put(room, setGuests);
					room = null;
					setGuests = new HashSet<Guest>();
				}

				room = new Room(roomNumber, roomCapacity, roomRate);
				rooms.add(room);

			} else if (currentLine.startsWith(LineFlag.G.toString() + " ")) {
				lineElems = currentLine.substring(2).split("[ ]+");
				guestFullName.append(lineElems[0] + " ").append(
						lineElems[1] + " ").append(lineElems[2]);
				Guest guest = new Guest(guestFullName.toString(), lineElems[3]);

				setGuests.add(guest);

				// input the last pair in the HashMap
				if (!input.hasNextLine()) {
					roomGuests.put(room, setGuests);
					room = null;
					setGuests = null;
				}
			} else {
				throw new IOException("Incorrect data from input file.");
			}
		}
	}

	@Override
	public String toString() {
		StringBuilder tempString = new StringBuilder();

		for (Map.Entry<Room, Collection<Guest>> pair : roomGuests.entrySet()) {
			tempString.append(pair.getKey() + "\n");
			Iterator<Guest> guestsIterator = pair.getValue().iterator();
			for (; guestsIterator.hasNext();) {
				tempString.append("\t" + guestsIterator.next() + "\n");
			}
			tempString.append("\n");

		}
		return tempString.toString();
	}

	public void printSortedRooms() {

	}

	public String toStringRooms() {
		StringBuilder resultString = new StringBuilder("Rooms in the hotel:\n");
		resultString.append(Room.toStringRooms(getRooms()));
		return resultString.toString();
	}
}
